//------------------------------------------------------------------------------
//  <copyright file="ControllerStateMachineRegisters.h" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//  </copyright>
//------------------------------------------------------------------------------

#pragma once

#include "stdafx.h"

/// <summary>
/// Class that handles lifetime of registers used by the ControllerExecutionEngine
/// to execute controller state machines.
/// <summary>
/// Type <c>T</c> indicates the type of value to be stored in the register.
template <class T>
class ControllerStateMachineRegisters
{
public:
	/// <summary>
	/// Initializes a new instance of the ControllerStateMachineDoubleRegisters class
	/// and allocates memory for the registers.
	/// <param name="registerCount"> Number of registers to initialize. </param>
	/// <param name="registerSizes"> Size of each register. </param>
	/// <returns> A pointer to an array containing pointers to each register. </returns>
	ControllerStateMachineRegisters(
		unsigned int registerCount,
		unsigned int pRegisterSizesInBytes[]);

	/// <summary> Destructor.  Frees the memory for each individual register </summary>
	~ControllerStateMachineRegisters();

	/// <summary> Get a pointer to the current register set. </summary>
	/// <returns> Pointer to the array of pointers for the current register set. </returns>
	T **GetRegisters();

	/// <summary> Copies the values stored in the registers to the supplied destination. </summary>
	/// <param name="ppDestination"> Array of pointers to the locations to store the copies of the registers. </param>
	void GetCopyOfRegisters(T **ppDestination);
	
	/// <summary> Get the pointer to an individual register. </summary>
	/// <returns> Poitner to the indicated register </returns>
	T *GetRegister(unsigned int registerNum);

	/// <summary> Gets the size of an individual register. </summary>
	/// <param name="registerNum"> Register to get the size of. </param>
	/// <returns> Size of the indicated register. </returns>
	unsigned int GetRegisterSizeInBytes(unsigned int registerNum);

	/// <summary> Gets an array with the sizes of all registers. </summary>
	/// <returns> An array containing the sizes of all registers. </returns>
	unsigned int *GetRegisterSizesInBytes();

	/// <summary> Gets the number of registers </summary>
	/// <returns> Number of registers. </returns>
	unsigned int GetRegisterCount();


	/// <summary> Copy values from one instance of <c>ControllerStateMachineRegisters</c> to another. </summary>
	/// <param name="pSource"> Register values source. </param>
	/// <param name="pDesitination"> 
	/// Destination. Must be allocated with registers of sufficient size for source to be copied.
	/// </param>
	static void CopyRegisters(ControllerStateMachineRegisters<T> *pSource, ControllerStateMachineRegisters<T> *pDestination);

private:
	/// <summary>
	/// An array of pointers to the individual registers.
	/// All registers are an array of one or more doubles.
	/// <summary>
	T **ppRegisters;

	/// <summary> Number of registers. </summary>
	unsigned int registerCount;

	/// <summary> Size of each register. </summary>
	unsigned int *pRegisterSizesInBytes;
};

/// <summary>
/// Initializes a new instance of the ControllerStateMachineRegisters class
/// and allocates memory for the registers based on the specified type.
/// <param name="registerCount"> Number of registers to initialize. </param>
/// <param name="registerSizes"> Size of each register. </param>
/// <returns> A pointer to an array containing pointers to each register. </returns>
template <class T>
ControllerStateMachineRegisters<T>::ControllerStateMachineRegisters(
	unsigned int registerCount,
	unsigned int pRegisterSizesInBytes[])
{
	unsigned int i = 0;
	unsigned int j = 0;

	this->ppRegisters = new T*[registerCount];
	this->registerCount = registerCount;
	this->pRegisterSizesInBytes = new unsigned int[registerCount];

	for (i = 0; i < registerCount; i++)
	{
		this->ppRegisters[i] = new T[pRegisterSizesInBytes[i] / sizeof(T)];
		this->pRegisterSizesInBytes[i] = pRegisterSizesInBytes[i];

		for(j = 0; j < pRegisterSizesInBytes[i] / sizeof(T); ++j)
		{
			this->ppRegisters[i][j] = 0;
		}
	}
}

/// <summary> Destructor.  Frees the memory for each individual register </summary>
template <class T>
ControllerStateMachineRegisters<T>::~ControllerStateMachineRegisters()
{
	unsigned int i;

	for (i = 0; i < this->registerCount; ++i)
	{
		delete this->ppRegisters[i];
	}

	delete this->ppRegisters;
}

/// <summary> Get a pointer to the current register set. </summary>
/// <returns> Pointer to the array of pointers for the current register set. </returns>
template <class T>
T **ControllerStateMachineRegisters<T>::GetRegisters()
{
	return this->ppRegisters;
}

/// <summary> Get the pointer to an individual register. </summary>
/// <returns> Poitner to the indicated register </returns>
template <class T>
T *ControllerStateMachineRegisters<T>::GetRegister(unsigned int registerNum)
{
	if(registerNum < this->registerCount)
	{
		return this->ppRegisters[registerNum];
	}

	throw "registerNum is not valid.";
}

/// <summary> Gets the size of an individual register. </summary>
/// <returns> Size of the indicated register. </returns>
template <class T>
unsigned int ControllerStateMachineRegisters<T>::GetRegisterSizeInBytes(unsigned int registerNum)
{
	if(registerNum < this->registerCount)
	{
		return this->pRegisterSizesInBytes[registerNum];
	}

	throw "registerNum is not valid.";
}

/// <summary> Gets an array with the sizes of all registers. </summary>
/// <returns> An array containing the sizes of all registers. </returns>
template <class T>
unsigned int *ControllerStateMachineRegisters<T>::GetRegisterSizesInBytes()
{
	return this->pRegisterSizesInBytes;
}

/// <summary> Gets the number of registers </summary>
/// <returns> Number of registers. </returns>
template <class T>
unsigned int ControllerStateMachineRegisters<T>::GetRegisterCount()
{
	return this->registerCount;
}

/// <summary>
/// Copy the register values from one <c>ControllerStateMachineRegisters</c> object to another.
/// Number and sizes of registers must match between source and destination.
/// </summary>
/// <param name="pSource"> Pointer to the source registers. </param>
/// <param name="pDestination"> Pointer to the destination registers. </param>
template <class T>
void ControllerStateMachineRegisters<T>::CopyRegisters(ControllerStateMachineRegisters<T> *pSource, ControllerStateMachineRegisters<T> *pDestination)
{
	if(pSource->GetRegisterCount() != pDestination->GetRegisterCount())
	{
		throw "Register counts are not equal between source and destination";
	}

	for(unsigned int i = 0; i < pSource->GetRegisterCount(); ++i)
	{
		if(pSource->GetRegisterSizeInBytes(i) != pDestination->GetRegisterSizeInBytes(i))
		{
			throw "Register sizes are not consistent between source and destination";
		}
	}

	T **destinationRegisters = pDestination->GetRegisters();
	pSource->GetCopyOfRegisters(destinationRegisters);
}

/// <summary> Copies the values stored in the registers to the supplied destination. </summary>
/// <param name="ppDestination"> Array of pointers to the locations to store the copies of the registers. </param>
template <class T>
void ControllerStateMachineRegisters<T>::GetCopyOfRegisters(T **ppDestination)
{
	for(unsigned int i = 0; i < this->registerCount; ++i)
	{
		memcpy(
			ppDestination[i],
			this->ppRegisters[i],
			this->pRegisterSizesInBytes[i]);
	}
}
